#ifndef TEXTQUERY_H
#define TEXTQUERY_H
#include "Util.h"
#include<iostream>
using std::cout;using std::endl;
#include<vector>
using std::vector;
#include<fstream>
using std::ifstream;using std::ofstream;
#include<sstream>
using std::istringstream;
#include<string>
using std::string;
#include<map>
#include<set>
#include<memory>
using std::shared_ptr; using std::make_shared;
#include<algorithm>
using std::find_if;

vector<char> endOfWord{' ',',',';','-',':','.','\n','\"'};
vector<char> endOfSent{'.','!',';'};
vector<string> namePrefix{"Mr","Mrs","Miss","Ms"};

class Query_base;
class QueryResult;
class TextQuery{
    friend QueryResult;
    friend Query_base;
    public:
        TextQuery() = default;
        TextQuery(const string &filePath);  //按文本文件路径构造
		QueryResult query(const string &inq) const;      //查询单词
        void showText();                    //展示文本内容
        ~TextQuery(){}
    protected:
        //内置的句子类
        struct Sentence;
        friend bool operator==(Sentence &lhs,Sentence &rhs);
        friend bool operator!=(Sentence &lhs,Sentence &rhs);
        friend bool operator<(Sentence &lhs,Sentence &rhs);
        friend bool operator>(Sentence &lhs,Sentence &rhs);
    private:
        shared_ptr<vector<Sentence>> file;    //vector按句子存储文本
        std::map<string,std::set<int>> whichSent;   //保存单词及其出现的句子序号
        void extractWord(const string &,int);     //从句子中提取单词给map
        bool checkSent(const string &);     //单词是否为句子
};

struct TextQuery::Sentence {                   //内置的句子类
    string contents;
    int line;
    Sentence() = default;
    Sentence(string s,int l):contents(s),line(l){}
    ~Sentence(){ }
};

bool operator==( TextQuery::Sentence &lhs, TextQuery::Sentence &rhs){
    return lhs.contents == rhs.contents;
}
bool operator!=( TextQuery::Sentence &lhs, TextQuery::Sentence &rhs){
    return !(lhs.contents == rhs.contents);
}
bool operator<(TextQuery::Sentence &lhs, TextQuery::Sentence &rhs){
    return lhs.contents < rhs.contents;
}
bool operator>(TextQuery::Sentence &lhs, TextQuery::Sentence &rhs){
    return !(lhs < rhs);
}
class QueryResult{  //查询结果类
    using Sentence = TextQuery::Sentence;  
    public: 
        QueryResult(string word,shared_ptr<std::set<int>> sp,shared_ptr<vector<Sentence>> file)
        :word(word),idxs(sp),file(file){}; 
        std::set<int>::iterator begin(){return idxs->begin();}
        std::set<int>::iterator end(){return idxs->end();}
        void print();        //打印词的涉及句子
        shared_ptr<vector<Sentence>> get_file(){ return file;}
    private:
        string word;    //所查询的单词
        shared_ptr<std::set<int>> idxs;    //所查词出现的句子序号
        shared_ptr<vector<Sentence>> file;   //原文本
        vector<string> processWord(string); //处理结合查询结果
        void highlightAt( string &,const string &); //针对句子的某个单词高亮
};

void QueryResult::print(){
    vector<string> wordList = processWord(word);
    if(DEBUG) {
        cout << "wordList:";
        for(string str : wordList){
            cout << str << " ";
        }
        cout << "\n";
    }
    for(int idx : *idxs){
        Sentence st = (*file)[idx];  
        string content = st.contents;
        auto hd = content.begin(), re = content.begin();
        for(string wd : wordList){
            highlightAt(content,wd);
        }
        printf("(line %4d) ",st.line);
        cout << content;
        cout << "\n";
    }
    cout << "query for " << word << " have " << idxs->size() << " records" << endl;
    
}

vector<string> QueryResult::processWord(string word){
    vector<string> wordList;
    auto hd = word.begin(),re = word.begin();
    while(re != word.end()){
        if(*re == '\n'){    //简便起见,句内换行按空格处理
            cout << " ";
            hd++;
            re++;
        }
        else if(!isalpha(*re)){
            hd++;
            re++;
        }      
        else{
            while(isalpha(*re))
                re++;
            if(!(*(hd - 1) == '~'))     
                wordList.push_back(tolowerStr(string(hd,re)));
            hd = re;
        }
    }
    return wordList;
}

void QueryResult::highlightAt(string &cont, const string &word){
    auto hd = cont.begin(), re = cont.begin();
    while(re != cont.end()){
        if(!isalpha(*re)){
            hd++;
            re++;
        }
        else{
            while(isalpha(*re))
                re++;
            string scanWord = string(hd,re);
            if(tolowerStr(word) == tolowerStr(scanWord)){
                cont.replace(hd,re,highlight(scanWord));
                break;
            }
            hd = re;
        }
    }
}

TextQuery::TextQuery(const string& filePath){
    ifstream infile(filePath);
    if(!infile){
        cout << "invaild file!" << endl;
        exit(-1);
    }
    file = make_shared<vector<Sentence>>();
    int curline = 1, startline = 1;
    string cont;
    char ch;
    bool newSent = false;

    auto loadSentDebug = [&](){
         if(DEBUG){
                cout << "new Sent loaded: "<< endl;
                cout << "line: " << startline << " ";
                cout << " idx: " << file->size() << " | ";
                cout << cont + ch ;
                cout << endl;
            }
    };

    while(infile.get(ch)){
        if(isupper(ch) && newSent == false){
            newSent = true;
            startline = curline;
        }
        else if(ch == '\n')
            curline++;
        else if(checkSent(cont) && newSent == true){
            extractWord(cont,file->size());
            loadSentDebug();
            file->push_back(Sentence(cont + ch,startline));
            cont = "";
            newSent = false;
        }
        if(newSent == true)
            cont += ch;
    }
    if(!cont.empty()){
        extractWord(cont,file->size());
        loadSentDebug();
        file->push_back(Sentence(cont,startline));
    }


}

QueryResult TextQuery::query(const string &inq) const{
	int cnt = 0;    //记录单词的出现次数
	string lowiq = tolowerStr(inq);  //将查询单词做大小写不敏感处理
	shared_ptr<std::set<int>> nodata(new std::set<int>);

    if(whichSent.find(lowiq) == whichSent.end()){
        cout << "no result" << endl;
        return QueryResult(lowiq,nodata,file);
    }
    auto loc = whichSent.find(lowiq);
    if(DEBUG){
        cout << " getQueryIdx: " << endl;
        for(int idx : loc->second){
            cout << idx << " ";
        }
        cout << endl;
    }
    shared_ptr<std::set<int>> data = make_shared<std::set<int>>(loc->second);
    return QueryResult(lowiq,data,file);
}   

void TextQuery::showText()
{
    cout << "show text: " << endl;
    for(Sentence st : *file){
        int line = st.line;
        printf("(line %4d) ",line);
        cout << st.contents;
        cout << endl;
    }
    cout << "text ends " << endl; 
}


void TextQuery::extractWord(const string &str,int idx){
    auto hd = str.begin(), re = str.begin();
    while(re != str.end() ){
        if(!isalpha(*re)){
            hd++;
            re++;
        }
        else{
            while(isalpha(*re)){
                re++;
            }
            whichSent[tolowerStr(string(hd,re))].emplace(idx);
            hd = re;
        }
    }
}

bool TextQuery::checkSent(const string &str){
	if (std::find(namePrefix.begin(), namePrefix.end(), trim(str)) != namePrefix.end())	//避免把姓名前缀(句号结尾)当句子
		return false;
	if (str.empty())    //避免漏入空字符
		return false;
	if (std::find(endOfSent.begin(), endOfSent.end(), *str.rbegin()) != endOfSent.end())	//判断为句子的条件是看字符串尾元素是否属于句子终结符集合内任一元素
		return true;
	return false;
}

#endif